package cn.ltpcloud.common.runner;

import cn.ltpcloud.common.annotation.Anonymous;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author jiejiebiezheyang
 * @since 2024-06-26 00:00
 */
public class AnonymousRunner {

    public final static List<String> ANONYMOUS = new ArrayList<>();

    static {
        ANONYMOUS.add("/favicon.ico");
        ANONYMOUS.add("/swagger-ui.html");
        ANONYMOUS.add("/swagger-ui/*");
        ANONYMOUS.add("/swagger-resources/**");
        ANONYMOUS.add("/v2/api-docs");
        ANONYMOUS.add("/v3/api-docs");
        ANONYMOUS.add("/v3/api-docs/**");
        ANONYMOUS.add("/doc.html");
        ANONYMOUS.add("/webjars/**");
        ANONYMOUS.add("/druid/**");
        ANONYMOUS.add("/actuator/**");
    }

    public static void run() throws Exception {
        Set<String> clazzSet = scanPackages("**.controller");

        // 遍历所有的 Bean
        for (String className : clazzSet) {
            Class<?> clazz = Class.forName(className);

            // 检查是否有 RestController 或者 Controller 注解
            if (clazz.isAnnotationPresent(RestController.class) || clazz.isAnnotationPresent(org.springframework.stereotype.Controller.class)) {
                String controllerPrefix = "";
                if (clazz.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                    String[] path = requestMapping.path();
                    if (path.length != 0) {
                        controllerPrefix = path[0] + "/";
                    } else {
                        String[] val = requestMapping.value();
                        if (val.length != 0) {
                            controllerPrefix = val[0] + "/";
                        }
                    }
                }

                Boolean has = clazz.isAnnotationPresent(Anonymous.class);

                Method[] methods = clazz.getDeclaredMethods();
                // 遍历所有的方法
                for (Method method : methods) {
                    boolean mHas = method.isAnnotationPresent(Anonymous.class);
                    if (!mHas && !has) {
                        continue;
                    }
                    StringBuilder prefix = new StringBuilder(controllerPrefix);
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping mapping = method.getAnnotation(RequestMapping.class);
                        String[] subPath = mapping.path();
                        if (subPath.length != 0) {
                            prefix.append(subPath[0]);
                        } else {
                            String[] val = mapping.value();
                            if (val.length != 0) {
                                prefix.append(val[0]);
                            }
                        }
                    }

                    if (method.isAnnotationPresent(GetMapping.class)) {
                        GetMapping mapping = method.getAnnotation(GetMapping.class);
                        String[] subPath = mapping.path();
                        if (subPath.length != 0) {
                            prefix.append(subPath[0]);
                        } else {
                            String[] val = mapping.value();
                            if (val.length != 0) {
                                prefix.append(val[0]);
                            }
                        }
                    }

                    if (method.isAnnotationPresent(PostMapping.class)) {
                        PostMapping mapping = method.getAnnotation(PostMapping.class);
                        String[] subPath = mapping.path();
                        if (subPath.length != 0) {
                            prefix.append(subPath[0]);
                        } else {
                            String[] val = mapping.value();
                            if (val.length != 0) {
                                prefix.append(val[0]);
                            }
                        }
                    }

                    if (method.isAnnotationPresent(DeleteMapping.class)) {
                        DeleteMapping mapping = method.getAnnotation(DeleteMapping.class);
                        String[] subPath = mapping.path();
                        if (subPath.length != 0) {
                            prefix.append(subPath[0]);
                        } else {
                            String[] val = mapping.value();
                            if (val.length != 0) {
                                prefix.append(val[0]);
                            }
                        }
                    }

                    if (method.isAnnotationPresent(PatchMapping.class)) {
                        PatchMapping mapping = method.getAnnotation(PatchMapping.class);
                        String[] subPath = mapping.path();
                        if (subPath.length != 0) {
                            prefix.append(subPath[0]);
                        } else {
                            String[] val = mapping.value();
                            if (val.length != 0) {
                                prefix.append(val[0]);
                            }
                        }
                    }

                    if (method.isAnnotationPresent(PutMapping.class)) {
                        PutMapping mapping = method.getAnnotation(PutMapping.class);
                        String[] subPath = mapping.path();
                        if (subPath.length != 0) {
                            prefix.append(subPath[0]);
                        } else {
                            String[] val = mapping.value();
                            if (val.length != 0) {
                                prefix.append(val[0]);
                            }
                        }
                    }
                    String path = prefix.toString();
                    path = path.replaceAll("//", "/").replaceAll(" ", "");
                    path = path.replaceAll("\\{[^}]+\\}", "*");
                    if (path.endsWith("/")) {
                        path = path.substring(0, path.length() - 1);
                    }
                    ANONYMOUS.add(path);
                }
            }
        }
    }

    private static Set<String> scanPackages(String basePackage) throws IOException, IOException {
        Set<String> classNames = new HashSet<>();

        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);

        scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> true);

        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(resolver);

        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                basePackage.replace('.', '/') + "/**/*.class";

        Resource[] resources = resolver.getResources(packageSearchPath);

        for (Resource resource : resources) {
            if (resource.isReadable()) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                String className = metadataReader.getClassMetadata().getClassName();
                classNames.add(className);
            }
        }

        return classNames;
    }
}
